﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.IO.Ports;
using System.Threading;

namespace PortSnf
{
    class Program
    {
        static string       _comA           = string.Empty;
        static string       _aliasPuertoA   = string.Empty;
        static string       _comB           = string.Empty;
        static string       _aliasPuertoB   = string.Empty;
        static int          _baudRate       = 0;
        static Handshake    _handShake      = Handshake.None;
        static SerialPort   _spA            = null;
        static SerialPort   _spB            = null;
        static object       _lock           = new object();

        static void Main (string [] args)
        {
            setTitle("HAVSoft PortSnf Simple");

            if (args.Length == 0)
                intereactiveSetting ();
            else if (! validarArgumentos (args))
                usage ();

            Console.WriteLine("PortSnf activated!");

            _spA = new SerialPort (_comA, _baudRate);
            _spA.Handshake = _handShake;
            _spA.Open ();
            _spA.DataReceived += new SerialDataReceivedEventHandler (dataReceived);

            _spB = new SerialPort (_comB, _baudRate);
            _spB.Handshake = Handshake.RequestToSend;
            _spB.Open ();
            _spB.DataReceived += new SerialDataReceivedEventHandler (dataReceived);

            //Thread t = new Thread(new ThreadStart(ff));
            //t.Start();

            Console.ReadLine();
        }

        //static void ff()
        //{
        //    for (; ; )
        //    {
        //        dataReceived(_spA, null);
        //        dataReceived(_spB, null);
        //        Thread.Sleep(1);
        //    }
        //}

        static void dataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            lock (_lock)
            {
                var esSerialPortA   = (sender == _spA);
                var sp              = sender as SerialPort;
                var spAltern        = esSerialPortA ? _spB : _spA;
                var toRead          = sp.BytesToRead;
                var buff            = new byte[toRead];
                sp.Read (buff, 0, buff.Length);
                hook (esSerialPortA, buff);
                spAltern.Write (buff, 0, buff.Length);
            }
        }

        static void hook (bool esSerialPortA, byte [] buff)
        {
            for (int i = 0; i < buff.Length; i += 4)
            {
                // title
                setBack(esSerialPortA ? ConsoleColor.Cyan : ConsoleColor.DarkCyan);
                setFore(ConsoleColor.Black);
                Console.Write("{0:HH:mm:ss.fff}", DateTime.Now);
                Console.Write(" ");
                Console.Write(esSerialPortA ? _aliasPuertoA : _aliasPuertoB);
                Console.Write(": ");

                // hexdump
                setBack(ConsoleColor.DarkGray);
                setFore(ConsoleColor.Black);
                StringBuilder sbHexa = new StringBuilder();
                for (int j = i; j < Math.Min(i + 4, buff.Length); j++)
                    sbHexa.Append(string.Format("{0:X2} ", buff[j]));
                Console.Write (" {0,-12} ", sbHexa);

                // ascii representation
                for (int j = i; j < Math.Min(i + 4, buff.Length); j++)
                {
                    setBack( ConsoleColor.Black );
                    string byteRepr = traducirByte(buff[j]);
                    setFore ((byteRepr.Length == 1) ? 
                        ConsoleColor.Gray : ConsoleColor.DarkGreen);
                    Console.Write(" {0,-3}", byteRepr);
                }

                // new line...
                Console.WriteLine();
            }
        }
        
        static void usage ()
        {
            Console.WriteLine("uso:");
            Console.WriteLine("     SniferSimple puerto1 puerto2 baudrate handshake");
            Console.ReadLine ();
            #if ! WindowsCE
            Environment.Exit (-1);
            #endif
        }

        static bool validarArgumentos (string [] args)
        {
            _comA = args[0].ToUpper();
            _comB = args[1].ToUpper();
            _baudRate = int.Parse(args[2]);
            return true;
        }

        static void intereactiveSetting ()
        {
            Console.Write ("port A name ? (eg. COM1): ");
            _comA = Console.ReadLine().ToUpper();

            Console.Write ("port A alias? (eg. xxxx): ");
            _aliasPuertoA = Console.ReadLine();

            Console.Write ("port B name ? (eg. COM2): ");
            _comB = Console.ReadLine().ToUpper();

            Console.Write ("port B alias? (eg. yyyy): ");
            _aliasPuertoB = Console.ReadLine();

            Console.Write ("baudrate    ? (eg. 9600): ");
            _baudRate = int.Parse(Console.ReadLine());

            Console.WriteLine("handshake? ");
            Console.WriteLine("  0) None");
            Console.WriteLine("  1) XOnXOff");
            Console.WriteLine("  2) Request to send (Hardware RTS CTS)");
            Console.WriteLine("  3) Request to send XonXOff");
            Console.Write    ("?: ");
             
            _handShake = (Handshake) int.Parse (Console.ReadLine());
        }

        static string traducirByte (byte b)
        {
            switch (b)
            { 
                case  0: return "NUL";
                case  1: return "SOH";
                case  2: return "STX";
                case  3: return "ETX";
                case  4: return "EOT";
                case  5: return "ENQ";
                case  6: return "ACK";
                case  7: return "BEL";
                case  8: return "BS ";
                case  9: return "TAB";
                case 10: return "LF ";
                case 11: return "VT ";
                case 12: return "FF ";
                case 13: return "CR ";
                case 14: return "SO ";
                case 15: return "SI ";
                case 16: return "DLE";
                case 17: return "DC1";
                case 18: return "DC2";
                case 19: return "DC3";
                case 20: return "DC4";
                case 21: return "NAK";
                case 22: return "SYN";
                case 23: return "ETB";
                case 24: return "CAN";
                case 25: return "EM ";
                case 26: return "SUB";
                case 27: return "ESC";
                case 28: return "FS ";
                case 29: return "GS ";
                case 30: return "RS ";
                case 31: return "US ";
                case 32: return "SPA";
                case 127:return "DEL";
                default :
                    return new string((char) b, 1);
            }
        }

        static void setBack(ConsoleColor cc)
        {
            #if ! WindowsCE
            Console.BackgroundColor = cc;
            #endif
        }

        static void setFore(ConsoleColor cc)
        {
            #if ! WindowsCE
            Console.ForegroundColor = cc;
            #endif
        }

        static void setTitle(string sTitle)
        {
            #if ! WindowsCE
            Console.Title = sTitle;
            #endif
        }
    }
}

namespace System
{
#if WindowsCE
    [Serializable]
    public enum ConsoleColor
    {
        Black       = 0,
        DarkBlue    = 1,
        DarkGreen   = 2,
        DarkCyan    = 3,
        DarkRed     = 4,
        DarkMagenta = 5,
        DarkYellow  = 6,
        Gray        = 7,
        DarkGray    = 8,
        Blue        = 9,
        Green       = 10,
        Cyan        = 11,
        Red         = 12,
        Magenta     = 13,
        Yellow      = 14,
        White       = 15,
    }
#endif
}
